using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.Reflection;
using System.IO;
using System.Windows;
using MindFactorial.LaunchPoint.Console;
using MindFactorial.LaunchPoint.Runtime;
using MindFactorial.LaunchPoint.Common;

namespace MindFactorial.LaunchPoint.Console.Library
{
    /// <summary>
    /// Represnts a console plugin
    /// </summary>
    internal class ConsolePlugIn : ConsoleObject, IConsolePlugIn
    {
        string _name = null, _label = null;
        PluginWindowCollection _windows = new PluginWindowCollection();
        Queue<IMessageProcessor> _messageTargets = new Queue<IMessageProcessor>();
        PluginControllerCollection _controllers = new PluginControllerCollection();
        PluginSurface _content = null, _context = null, _toolbar = null;
        PluginMenu _menu = null;
        //PluginMessagingPipeline _pipeline = null;
        PluginConfig _initparams = null;
        LaunchPointRuntimeContext _runtimecontext = null;
        IRootWindow _shell;
        bool _isActive = false;
        

        private ConsolePlugIn(string name, string label, IRootWindow shell)
        {
            this._name = name;
            this._label = label;
            this._shell = shell;
            _runtimecontext = new LaunchPointRuntimeContext( _shell.RunnableContext);
            
        }

        /// <summary>
        /// activates the plugin
        /// </summary>
        public void Activate()
        {
            this._isActive = true;
        }

        /// <summary>
        /// Deactivates the plugin
        /// </summary>
        public void Deactivate()
        {
            this._isActive = true;

            //calls deactivate on the three main areas as well as the menu
            this.ContentAreaSurfacePresenter.Deactivate();
            this.ContextAreaSurfacePresenter.Deactivate();
            this.ToolbarAreaSurfacePresenter.Deactivate();

            
            
        }

        /// <summary>
        /// Gets a value indicating whether the plugin is active or not
        /// </summary>
        public bool IsActive
        {
            get
            {
                return this._isActive;
            }
        }


        /// <summary>
        /// gets the toolbar surface 
        /// </summary>
        internal PluginSurface ToolbarAreaSurfacePresenter
        {
            get
            {
                return _toolbar;
            }
            
        }

        /// <summary>
        /// gets the content surface
        /// </summary>
        internal PluginSurface ContentAreaSurfacePresenter
        {
            get
            {
                return _content;
            }
            
        }

        /// <summary>
        /// gets the contextarea surface
        /// </summary>
        internal PluginSurface ContextAreaSurfacePresenter
        {
            get
            {
                return _context;
            }
           
        }

        /// <summary>
        /// gets the top level menu presenter
        /// </summary>
        internal IMenuPresenter TopLevelMenuPresenter
        {
            get
            {
                return (IMenuPresenter)_menu;
            }
        }

        internal PluginConfig InitParams
        {
            get
            {
                return _initparams;
            }
            
        }

        

        /// <summary>
        /// gets the collection of window manager objects.  
        /// Window manager objects are used to create new instances of the 
        /// windows they represent and not to be directly displayed using Show()
        /// </summary>
        internal PluginWindowCollection Windows
        {
            get { return _windows; }
        }

        /// <summary>
        /// Gets the list of all controllers for this plugin.  Controllers
        /// are simply non UI objects loaded into memory at console load time
        /// which are allowed to participate in messaging
        /// </summary>
        internal PluginControllerCollection Controllers
        {
            get
            {
                return _controllers;
            }
        }        

             
        /// <summary>
        /// gets the unique name of the plugin
        /// </summary>
        public string Name
        {
            get
            {
                return _name;
            }
        }

        /// <summary>
        /// gets the label (found in the registration area) of the plugin
        /// </summary>
        public string Label
        {
            get
            {
                return _label;
            }
        }

        /// <summary>
        /// gets the messaging pipeline for communication with the console, LaunchPoint runtime,
        /// and other plugins
        /// </summary>
        public PluginMessagingPipeline MessagingPipeline
        {
            get
            {
                return _shell.MessagingPipeline;
            }
        }

        /// <summary>
        /// Gets a context to the LaunchPoint runtime
        /// </summary>
        public LaunchPointRuntimeContext RuntimeContext
        {
            get
            {
                return _runtimecontext;
            }
        }

        /// <summary>
        /// Shuts down the console
        /// </summary>
        internal void ShutDown()
        {
            //close all windows
            foreach (PluginWindow window in _windows)
            {
                window.OnShutDown();
                window.Close();
            }


        }

        /// <summary>
        /// dispatches messages to all participating components within a plugin
        /// </summary>
        /// <param name="filter"></param>
        /// <param name="message"></param>
        public void RouteMessage<T>(string filter, T message)
        {
            foreach (IMessageProcessor messagetarget in _messageTargets)
            {
                messagetarget.ProcessMessage(filter, message);
            }
        }

        #region Plugin Load Logic
        /// <summary>
        /// Loads a built in well known plugin like the factory explorer
        /// </summary>
        /// <param name="name"></param>
        /// <param name="label"></param>
        /// <param name="shell"></param>
        /// <param name="pluginAssembly"></param>
        /// <returns></returns>
        internal static ConsolePlugIn LoadPluginAsBuiltIn(string name, string label, IRootWindow shell, Assembly pluginAssembly)
        {
            ConsolePlugIn plugin = new ConsolePlugIn(name, label, shell);
            Load(pluginAssembly, ref plugin);
            return plugin;
        }

        /// <summary>
        /// Loads the plugin
        /// </summary>
        /// <param name="pluginnode">node from config file</param>
        /// <param name="shell">shell that serves as the root window of the plugin</param>
        /// <returns></returns>
        internal static ConsolePlugIn LoadPlugin(PluginConfig pluginnode, IRootWindow shell)
        {
            try
            {
                //read properties from config file
                string name = pluginnode.Name ;
                string label = pluginnode.Label ;
                string path = pluginnode.Path ;
                string filename = Path.GetFileName(path);

                //get assembly name
                AssemblyName assemblyName = AssemblyName.GetAssemblyName(path);

                //load assembly into current domain
                Assembly pluginAssembly = AppDomain.CurrentDomain.Load(assemblyName);

                ConsolePlugIn plugin = new ConsolePlugIn(name, label, shell);

                plugin._initparams = pluginnode;

                //do actual loading
                Load(pluginAssembly, ref plugin);


                return plugin;
            }
            catch (Exception ex)
            {
                throw new LaunchPointRuntimeException("55B9D9AF-9673-4fc4-8187-87A744385DC8: Could not load plugin with definition name:" + pluginnode.Name + " because " + ex.Message);
            }
        }

        private static void Load(Assembly pluginAssembly, ref ConsolePlugIn plugin)
        {
            try
            {
                Type[] types = pluginAssembly.GetTypes();
                //iterate twice (first time is to ensure that plugin surfaces are ALWAYS loaded BEFORE
                //anything else is loaded
                foreach (Type type in types)
                {
                    //first load all surface presenters from assembly
                    LoadSurfacePresenters(pluginAssembly, ref plugin, type);

                    //next load all windows
                    LoadPluginWindows(pluginAssembly, ref plugin, type);

                    //then load controllers
                    LoadPluginMenus(pluginAssembly, ref plugin, type);


                }

                //now laod the controllers (I know it sucks to go throug the loop twice
                //so change it if you have a better idea)  Regardless we must ensure that
                //the controller is only loaded AFTER all other surfaces and what not are loaded
                //as the init method could potentially reference them: It has been represented as
                //a SAFE place to reference other surfaces through the 
                foreach (Type type in types)
                {
                    //finally try to load menu objects
                    LoadControllers(pluginAssembly, ref plugin, type);
                }
            }
            catch (ReflectionTypeLoadException tex)
            {
                Logger.Log("771C2FC0-2168-458c-85D7-EC67D99ACE96", tex);
                throw new LaunchPointRuntimeException("B2D14E6F-F0BF-4d3c-B77F-5AD8AD9025BA: Could not load types from assembly " + pluginAssembly.FullName);
            }

        }



        /// <summary>
        /// Attempts to create a controller from the given type.  If the type has not been decorated with
        /// the PluginController attribute the type is ignored.
        /// </summary>
        /// <param name="pluginAssembly"></param>
        /// <param name="plugin"></param>
        /// <param name="type"></param>
        private static void LoadControllers(Assembly pluginAssembly, ref ConsolePlugIn plugin, Type type)
        {
            //find and instantiate all plugin surface presenters
            object[] attributes = type.GetCustomAttributes(typeof(PluginControllerAttribute), false);
            if (attributes.Length > 0)
            {
                //load the instance of the controller
                PluginController controller = pluginAssembly.CreateInstance(type.FullName) as PluginController;
                if (controller != null)
                {
                    controller.PluginContext = plugin;

                    //set all ui state first
                    controller.SetUIState(
                        plugin.ToolbarAreaSurfacePresenter,
                        plugin.ContextAreaSurfacePresenter,
                        plugin.ContentAreaSurfacePresenter,
                        plugin.TopLevelMenuPresenter);

                    //call init for any advanced initialization based on when console starts
                    //and not when plugin is activated
                    controller.Init();

                    //load the list of controllers
                    PluginControllerAttribute controllerAttribute = (PluginControllerAttribute)attributes[0];

                    //set the name of the controller
                    controller.SetName(controllerAttribute.Name);

                    //add controller to plugin controllers collection
                    plugin._controllers.Add(controller);

                    //register all message target surfaces
                    if (controller is IMessageProcessor)
                        plugin._messageTargets.Enqueue((IMessageProcessor)controller);



                }
            }
            else
                MindFactorial.LaunchPoint.Common.Logger.Log("E00C6A17-0BC8-4135-B644-F8F76DFEA82C: Error loading type {0} from assembly {1} into console", type.FullName, pluginAssembly.FullName);
        }

        /// <summary>
        /// Loads any surfaces from the given assembly
        /// </summary>
        /// <param name="pluginAssembly"></param>
        /// <param name="plugin"></param>
        /// <param name="type"></param>
        private static void LoadSurfacePresenters(Assembly pluginAssembly, ref ConsolePlugIn plugin, Type type)
        {
            //find and instantiate all plugin surface presenters
            object[] attributes = type.GetCustomAttributes(typeof(PluginSurfacePresenterAttribute), false);
            if (attributes.Length > 0)
            {
                PluginSurface surface = pluginAssembly.CreateInstance(type.FullName) as PluginSurface;
                if (surface != null)
                {
                    surface.Label = plugin._label;

                    surface.PluginContext = plugin;
                    PluginSurfacePresenterAttribute surfaceAttribute = (PluginSurfacePresenterAttribute)attributes[0];
                    switch (surfaceAttribute.Target)
                    {
                        case SurfaceTarget.CommandContext:
                            plugin._context = surface;
                            break;
                        case SurfaceTarget.Content:
                            plugin._content = surface;
                            break;
                        case SurfaceTarget.Toolbar:
                            plugin._toolbar = surface;
                            break;

                    }


                    //register all message target surfaces
                    if (surface is IMessageProcessor)
                        plugin._messageTargets.Enqueue((IMessageProcessor)surface);
                }
                else
                    MindFactorial.LaunchPoint.Common.Logger.Log("4429A560-5C3B-4377-BB16-E037D452B446: Error loading type {0} from assembly {1} into console", type.FullName, pluginAssembly.FullName);
            }
        }

        /// <summary>
        /// Loads any windows from the given assembly
        /// </summary>
        /// <param name="pluginAssembly"></param>
        /// <param name="plugin"></param>
        /// <param name="type"></param>
        private static void LoadPluginWindows(Assembly pluginAssembly, ref ConsolePlugIn plugin, Type type)
        {
            object[] attributes = type.GetCustomAttributes(typeof(PluginWindowPresenterAttribute), false);
            if (attributes.Length > 0)
            {
                PluginWindow window = pluginAssembly.CreateInstance(type.FullName) as PluginWindow;
                if (window != null)
                {
                    window.PluginContext = plugin;
                    window.Init();
                    PluginWindowPresenterAttribute windowAttribute = (PluginWindowPresenterAttribute)attributes[0];

                    //add window to plugin window collection
                    plugin._windows.Add(windowAttribute.Name, window);

                    //register all message target surfaces
                    if (window is IMessageProcessor)
                        plugin._messageTargets.Enqueue((IMessageProcessor)window);
                }
                else
                    MindFactorial.LaunchPoint.Common.Logger.Log("44D075AD-2062-46e7-9071-8715AF3E9591: Error loading type {0} from assembly {1} into console", type.FullName, pluginAssembly.FullName);
            }
        }

        /// <summary>
        /// Loads the plugin menus
        /// </summary>
        /// <param name="pluginAssembly"></param>
        /// <param name="plugin"></param>
        /// <param name="type"></param>
        private static void LoadPluginMenus(Assembly pluginAssembly, ref ConsolePlugIn plugin, Type type)
        {
            object[] attributes = type.GetCustomAttributes(typeof(PluginMenuPresenterAttribute), false);
            if (attributes.Length > 0)
            {
                PluginMenu menu = pluginAssembly.CreateInstance(type.FullName) as PluginMenu;
                if (menu != null)
                {
                    menu.PluginContext = plugin;
                    PluginMenuPresenterAttribute menuAttribute = (PluginMenuPresenterAttribute)attributes[0];

                    //add menu to plugin menu collection
                    plugin._menu = menu;

                    //register all message target surfaces
                    if (menu is IMessageProcessor)
                        plugin._messageTargets.Enqueue((IMessageProcessor)menu);
                }
                else
                    MindFactorial.LaunchPoint.Common.Logger.Log("622AE521-60DA-4e87-9E91-726ADAC2D57A: Error loading type {0} from assembly {1} into console", type.FullName, pluginAssembly.FullName);
            }
        }

        #endregion


      

        public void ShowWaitUI()
        {
            _shell.RunnableContext.ShowWaitUI();
        }

        public void HideWaitUI()
        {
            _shell.RunnableContext.HideWaitUI();
        }

        public void ShowWaitUI(object wait_context)
        {
            _shell.RunnableContext.ShowWaitUI(wait_context);
        }

        public void HideWaitUI(object wait_context)
        {
            _shell.RunnableContext.HideWaitUI(wait_context);
        }

     
    }
}
